/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
 *
 */

import { normalWorker, watchWorker } from '@ohos/hvigor';
import { FileSet } from '@ohos/hvigor';
import { hvigorCore } from '@ohos/hvigor';
import { Level } from 'log4js';
import path from 'path';
import { cloneDeep } from '@baize/wdk';

import { AbilityConst, PageType } from '../const/common-const.js';
import { OhosLogger } from '../utils/log/ohos-logger.js';

import { TaskNames } from './common/task-names.js';
import { ArkCompile } from './ark-compile.js';
import { PreviewUpdateAssets } from './preview-update-assets.js';
import CommonTask = TaskNames.CommonTask;
import { ApiVersion } from '../const/sdk-const.js';
import { limitMinApiVersion, warnCreateBuildProfileTask } from '../utils/task-util.js';
import { getJson5Obj } from '../utils/json-util.js';
import fse from 'fs-extra';
import { ModuleJson } from '../options/configure/module-json-options.js';
import AbilityObj = ModuleJson.AbilityObj;
import { BuildDirConst } from '../const/build-directory-const.js';
import { generateOhmUrlForSourceFile, generateOhmurlForSrcEntry } from '../utils/ohmurl-utils.js';

export class PreviewerArkCompile extends ArkCompile {
  logger: OhosLogger = OhosLogger.getLogger(PreviewerArkCompile.name);

  declareInputFiles(): FileSet {
    return this.moduleModel.isHarModule() ?
      super.declareInputFiles().deleteEntry(this.aceBuildJsonDir) : super.declareInputFiles();
  }

  protected async doTaskAction(): Promise<void> {
    const pageType = hvigorCore.getExtraConfig().get('pageType');
    const compileConfig = await this.getPreviewCompileConfig(this.logger);
    const workerFileName = path.resolve(__dirname, './worker/preview-build.js');
    const compileBlockPkg = this.getBlockDependencies().map(value => value.getDependencyName());
    compileConfig.pkgNameToPkgBriefInfo = this.pkgNameToPkgBriefInfo;
    compileConfig.otherPaths = this.getTscAddressPaths(compileConfig.aceModuleRoot);
    compileConfig.compileBlockPkg = compileBlockPkg;
    if (!pageType || pageType === PageType.PAGE) {
      // 为了提升编译速度，entry依赖hsp(s)预览entry时在一条预览命令里面同时编译entry和hsp。
      // hap引用hsp(s)预览hap时，用normalWorker编译hsp(s),用watchWorker编译hap。
      // 目前多个worker线程同时watch时，多个线程并发访问资源（如读取文件）可能存在竞争问题，所以hsp worker 线程编译结束后要主动 terminate 掉。
      const isCompileHspDependency = hvigorCore.getExtraConfig().get('compileHspDependencies') === 'true';
      const mainModule = hvigorCore.getExtraConfig().get('mainModule'); // 本次预览的主模块
      const useNormalWorker = isCompileHspDependency && mainModule !== this.moduleName;
      const previewWorker = useNormalWorker ? normalWorker : watchWorker;
      const workerId = previewWorker.createWorker(this, workerFileName, true,
        (compileEvents: object[]) => this.handleCompileEvents(compileEvents, this.getSubDurationEvent(workerId), true),
        {
          workerData: {
            projectConfig: compileConfig,
            logLevel: (this.logger.getLevel() as Level).levelStr,
          }
        });
    }

    if (this.needSubmitArkTsWidget && pageType === PageType.CARD) {
      const widgetCompileConfig = {
        ...compileConfig,
        widgetCompile: 'true',
        cachePath: path.resolve(compileConfig.cachePath ?? this.getTaskTempDir(this.targetData, '_widget'), 'widget'),
        port: undefined // set undefined to avoid conflict from normal preview
      };
      const workerId = watchWorker.createWorker(this, workerFileName, true,
        (compileEvents: object[]) => this.handleCompileEvents(compileEvents, this.getSubDurationEvent(workerId), true),
        {
          workerData: {
            projectConfig: widgetCompileConfig,
            logLevel: (this.logger.getLevel() as Level).levelStr,
          }
        });
    }
    this.addOhmurlToHarAbility();
    warnCreateBuildProfileTask(this.targetData, this.targetService, this._log);
  }

  /**
   * 处理module.json: 将ohmurl字段添加到收集到的har的abilities、extensionAbilities
   * @private
   */
  private addOhmurlToHarAbility() {
    const moduleJsonObj = getJson5Obj(this.pathInfo.getPreviewIntermediatesResModuleJsonPath());
    const moduleNodeDir = this.moduleModel.getModule().getNodeDir();
    // 处理abilities、extensionAbilities
    for (const abilityTypeName of [AbilityConst.UI_ABILITY, AbilityConst.EXTENSION_ABILITY]) {
      const moduleAbilities = moduleJsonObj?.module?.[abilityTypeName] as ModuleJson.AbilityObj[] | undefined;
      if (!moduleAbilities?.length) {
        continue;
      }

      // 获取所有har依赖里的处理过的ability、extensionAbilities，替换到当前模块的ability、extensionAbilities
      const dependencyHarAbility = this.getDependencyHarAbility(abilityTypeName);
      moduleAbilities?.forEach((abilityObj: AbilityObj) => {
        if (!abilityObj.srcEntry) {
          return;
        }

        const moduleAbsoluteSrcEntry = path.resolve(moduleNodeDir, BuildDirConst.SRC_MAIN, abilityObj.srcEntry);
        Object.assign(abilityObj, {
          ohmurl: generateOhmUrlForSourceFile(
            this.targetService.getPkgContextInfo(this.packageJsonObj.name),
            moduleAbsoluteSrcEntry.substring(moduleNodeDir.length + 1)
          )
        });

        const depHarAbilityObj = dependencyHarAbility.find((depAbilityObj: AbilityObj) => depAbilityObj.name === abilityObj.name);
        if (depHarAbilityObj) {
          Object.assign(abilityObj, depHarAbilityObj);
        }
      });

      Object.assign(moduleJsonObj.module, {
        [abilityTypeName]: moduleAbilities
      });
    }

    fse.writeFileSync(this.pathInfo.getPreviewIntermediatesResModuleJsonPath(), JSON.stringify(moduleJsonObj));
  }

  /**
   * 预览调试场景收集hap/hsp依赖的har的ability配置 修改其srcEntry为相对于hap/hsp的module.json的相对路径且需要添加ohmurl字段
   * @param {string} abilityTypeName
   * @private
   */
  private getDependencyHarAbility(abilityTypeName: string) {
    const hostModuleSrcMainPath = path.resolve(this.moduleModel.getModule().getNodeDir(), BuildDirConst.SRC, BuildDirConst.MAIN);
    const dependencyAbilityObj: AbilityObj[] = [];
    this.service.getHarDependencies().forEach((dependency) => {
      const dependencyModuleJsonObj = dependency.getModuleJsonObj();
      const dependencyModuleAbilities = dependencyModuleJsonObj?.module[abilityTypeName] as ModuleJson.AbilityObj[] | undefined;
      if (!dependencyModuleAbilities?.length) {
        return;
      }

      dependencyModuleAbilities.forEach((abilityObj: AbilityObj) => {
        const dependencyAbilities = cloneDeep(abilityObj);
        if (!dependencyAbilities.srcEntry) {
          return;
        }

        // 合并依赖模块abilities
        const dependencyAbsoluteSrcEntry = path.resolve(dependency.getDependencyRootPath(), BuildDirConst.SRC, BuildDirConst.MAIN, dependencyAbilities.srcEntry);
        Object.assign(dependencyAbilities, {
          srcEntry: path.relative(hostModuleSrcMainPath, dependencyAbsoluteSrcEntry),
          ohmurl: generateOhmurlForSrcEntry(this.targetService, this.moduleName, dependency, dependencyAbilities.srcEntry)
        });

        dependencyAbilityObj.push(dependencyAbilities);
      });
    });

    return dependencyAbilityObj;
  }

  initTaskDepends(): void {
    this.declareDepends(PreviewUpdateAssets.name);
    if (limitMinApiVersion(this.targetData, ApiVersion.API_VERSION_9)) {
      this.declareDepends(CommonTask.CREATE_BUILD_PROFILE.name);
    }
  }
}
